package com.core.netty.http;

import com.core.comment.StringUtil;
import com.core.netty.http.work.*;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;


/**
 * http 请求处理器
 * 2018/11/1.
 */

@ChannelHandler.Sharable
public class HttpRequestHandler extends SimpleChannelInboundHandler<HttpObject> {


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject hob) throws Exception {


        // HTTP客户端程序有一个实体的主体部分要发送给服务器，但希望在发送之前查看下服务器是否会
        // 接受这个实体，所以在发送实体之前先发送了一个携带100 Continue的Expect请求首部的请求。
        // 服务器在收到这样的请求后，应该用 100 Continue或一条错误码来进行响应。
        // Laruence的博客写到：在使用curl做POST的时候, 当要POST的数据大于1024字节的时候, curl并不会直接就发起POST请求, 而是会分为俩步,
        // 1. 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
        // 2. 接收到Server返回的100-continue应答以后, 才把数据POST给Server
        FullHttpRequest req = (FullHttpRequest) hob;
        String msg = null;

        HttpMyReponse myrep = null;
        try {
            myrep = handleRequest(ctx, req);
            if (myrep == null) {
                myrep = new HttpMyReponse();
                myrep.setCtx(ctx);
            }
        } catch (Exception e) {

            myrep.fullWrite("500,服务器报错啦！");
        }
        myrep.fullWrite(null);

        //String content = ((HttpContent) hob).content().toString(Charset.forName("utf-8"));
        //System.out.println("The body is : " + content);

//       if (is100ContinueExpected(req)) {
//          ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
//       }

    }

    /**
     * 处理请求头 处理 cookie 和 session
     *
     * @param headers
     */
    private void handleHeaders(HttpHeaders headers, HttpMyRequest req, HttpMyReponse rep) {
        req.setHeaders(headers);

        String cookie = headers.get("Cookie");
        String sessionid = null;
        //取cookie里的sessid 把cookie填充到request里
        try {
            if (StringUtil.isNotBlack(cookie)) {
                String[] cookies = cookie.split(";");
                for (String c : cookies) {
                    if (!"HttpOnly".equals(c) && c.contains("=")) {
                        String[] cs = c.split("=");
                        req.setCookie(cs[0] != null ? cs[0].trim() : null, cs[1] != null ? cs[1].trim() : null);
                        if (cs[0] != null && "mysessionid".equals(cs[0].trim())) {
                            sessionid = cs[1] != null ? cs[1].trim() : null;

                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        //生成session 或 取sesssion
        if (SessionUtil.get(sessionid) == null) {
            sessionid = StringUtil.getUuid();
            HttpMySession se = new HttpMySession();
            se.setSessionId(sessionid);
            SessionUtil.put(sessionid, se);
            req.setSession(se);
            rep.setHeader("Set-Cookie", "mysessionid=" + sessionid + "; Path=/; HttpOnly");
        } else {
            HttpMySession session = SessionUtil.get(sessionid);
            req.setSession(session);
        }
    }

    /**
     * 处理请求
     *
     * @param ctx
     * @param req
     * @return
     * @throws Exception
     */
    private HttpMyReponse handleRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
        HttpMyRequest myreq = new HttpMyRequest();
        HttpMyReponse myrep = new HttpMyReponse();
        //处理请求头
        HttpHeaders headers = req.headers();
        handleHeaders(headers, myreq, myrep);
        //回复对象填充ctx
        myrep.setCtx(ctx);
        //取请求参数
        Map<String, String> parmMap = new RequestParser(req).parse(); // 将GET, POST所有请求参数转换成Map对象
        myreq.initPars(parmMap);
        //取请求的ip和端口和请求方法
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        int port = insocket.getPort();


        //填充request
        myreq.setRemoteHost(ip);
        myreq.setPort(port);
        myreq.setMethod(req.method().name());


        //取请求url
        String uri = req.uri();
        uri = uri.replaceAll("/+", "/");

        myreq.setUrl(uri);
        //取url相应的serlvet 填充并执行
        String msg;
        HttpMySerlvet mySer = getSerlvet(uri);
        if (mySer != null) {

            //执行请求的执行方法
            mySer.domainBefor(myreq, myrep);
            mySer.domain(myreq, myrep);
            mySer.domainAfter();
            //取出回应的字符串
            msg = mySer.getMyrep().getMsg();

        } else {
            msg = "二百五，URL不对！";
        }
        return myrep;
    }

    /**
     * 获取serlvet
     *
     * @param uri 请求路径
     * @return
     */
    private HttpMySerlvet getSerlvet(String uri) {
        HttpMySerlvet mySer = null;
        if (SerlvetUtil.get(uri) != null) {
            mySer = (HttpMySerlvet) SerlvetUtil.get(uri);
        } else {
            for (Map.Entry<String, MySerlvet> entry : SerlvetUtil.getMap().entrySet()) {
                String key = entry.getKey();
                if (key.contains("*")) {
                    if (key.endsWith("*") && uri.contains(key.substring(0, key.indexOf("*")))) {
                        mySer = (HttpMySerlvet) entry.getValue();
                    }

                }

            }
        }
        return mySer;
    }


    public static boolean is100ContinueExpected(HttpMessage message) {
        if (!isExpectHeaderValid(message)) {
            return false;
        } else {
            String expectValue = (String) message.headers().get("expect");
            return HttpHeaderValues.CONTINUE.toString().equalsIgnoreCase(expectValue);
        }
    }

    private static boolean isExpectHeaderValid(HttpMessage message) {
        return message instanceof HttpRequest && message.protocolVersion().compareTo(HttpVersion.HTTP_1_1) >= 0;
    }


}